package edu.codink.xq.center.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

@SuppressWarnings("unchecked")
public class HibernateDao extends HibernateDaoSupport
{

	/**
	 * 根据主键查询实体
	 * 
	 * @param entityClass
	 *            实体ClassType
	 * @param id
	 *            实体主键
	 * @return 实体
	 */
	public Object get(Class entityClass, Serializable id)
	{
		return getHibernateTemplate().get(entityClass, id);
	}

	/**
	 * 根据ClassType查询所有实体
	 * 
	 * @param entityClass
	 *            实体ClassType
	 * @return 实体集合
	 * @note spring额外的API，底层是hibernate的Criteria实现的
	 */
	public List loadAll(Class entityClass)
	{
		return getHibernateTemplate().loadAll(entityClass);
	}

	/**
	 * 保存实体
	 * 
	 * @param entity
	 *            实体模型
	 * @return insert之后该实体的主键
	 */
	public Serializable save(Object entity)
	{
		return getHibernateTemplate().save(entity);
	}

	/**
	 * 更新实体
	 * 
	 * @param entity
	 *            实体模型
	 * @note 如果失败将throw DataAccessException
	 */
	public void update(Object entity)
	{
		getHibernateTemplate().update(entity);
	}

	/**
	 * 删除实体
	 * 
	 * @param entity
	 *            实体模型
	 * @note 如果失败将throw DataAccessException
	 */
	public void delete(Object entity)
	{
		getHibernateTemplate().delete(entity);
	}

	/**
	 * 实体动态属性查询
	 * 
	 * @param entityClass
	 *            实体模型
	 * @param propertyNames
	 *            条件属性
	 * @param parameter
	 *            条件值
	 * @return 符合条件的实体集合
	 */
	public List findByProperty(Class entityClass, String[] propertyNames,
			Object[] parameter)
	{
		String className = entityClass.getName();
		StringBuilder builder = new StringBuilder();
		builder.append("from ").append(className).append(" as obj where ");
		for (int i = 0; i < propertyNames.length; i++)
		{
			builder.append("obj.").append(propertyNames[i]).append("=? ");
			// 如不是最后一个参数，就加and
			if (i != propertyNames.length - 1)
			{
				builder.append("and ");
			}
		}
		return getHibernateTemplate().find(builder.toString(), parameter);
	}

	/**
	 * 创建基本的Criteria
	 * 
	 * @param entityClass
	 *            实体的ClassType
	 * @param criterions
	 *            Criterion条件集合
	 * @return Criteria
	 */

	private Criteria createCriteria(Session session, Class entityClass,
			Criterion... criterions)
	{
		Criteria criteria = session.createCriteria(entityClass);
		for (Criterion criterion : criterions)
		{
			criteria.add(criterion);
		}
		return criteria;
	}

	/**
	 * 创建排序条件的Criteria
	 * 
	 * @param entityClass
	 *            实体的ClassType
	 * @param orderProperty
	 *            排序属性集合(支持多字段排序)
	 * @param isAsc
	 *            排序方式集合(默认升序,false为Desc)
	 * @param criterions
	 *            Criterion条件集合
	 * @return Criteria
	 */

	private Criteria createCriteria(Session session, Class entityClass,
			String[] orderProperty, boolean[] isAsc, Criterion... criterions)
	{

		if (orderProperty.length == isAsc.length)
		{
			Criteria criteria = createCriteria(session, entityClass, criterions);
			for (int i = 0; i < orderProperty.length; i++)
			{
				if (isAsc[i])
				{
					criteria.addOrder(Order.asc(orderProperty[i]));
				} else
				{
					criteria.addOrder(Order.desc(orderProperty[i]));
				}
			}
			return criteria;
		} else
		{
			return null;
		}
	}

	/**
	 * 根据Criteria查询实体
	 * 
	 * @param entityClass
	 * @param orderProperty
	 * @param isAsc
	 * @param criterions
	 * @return
	 */

	public List findByCriteria(final Class entityClass,
			final String[] orderProperty, final boolean[] isAsc,
			final Criterion... criterions)
	{
		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				return createCriteria(session, entityClass, orderProperty,
						isAsc, criterions).list();
			}
		});
	}

	public List findByCriteria(final Class entityClass,
			final Criterion... criterions)
	{
		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				return createCriteria(session, entityClass, criterions).list();
			}
		});
	}

	/**
	 * 根据Criteria分页查询实体(排序)
	 * 
	 * @param totalCount
	 *            总记录数，可用getCountByCriteria(Criteria)获得，目的提高性能，可以复用totalCount
	 * @param criteria
	 *            查询条件
	 * @param pageSize
	 *            每页记录数
	 * @param startIndex
	 *            每页记录的开始索引，可用Page类的getNextIndex获得
	 * @param orderProperty
	 *            排序的属性
	 * @param isAsc
	 *            排序顺序
	 * @return Page对象
	 */

	public Page findPageByCriteria(final int totalCount,
			final Criteria criteria, final int pageSize, final int startIndex,
			final String orderProperty, final boolean isAsc)
	{
		return (Page) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session arg0)
					throws HibernateException, SQLException
			{
				// 清除criteria中rowcount条件，为下面查询数据作准备
				criteria.setProjection(null);
				// 判断是否需要排序
				if (!(orderProperty == null || orderProperty.equals("")))
				{
					if (isAsc)
					{
						criteria.addOrder(Order.asc(orderProperty));
					} else
					{
						criteria.addOrder(Order.desc(orderProperty));
					}
				}
				List items = criteria.setFirstResult(startIndex).setMaxResults(
						pageSize).list();
				Page page = new Page(items, totalCount, pageSize, startIndex);
				return page;
			}
		});

	}

	/**
	 * 根据Criteria分页查询实体
	 * 
	 * @param totalCount
	 *            总记录数，可用getCountByCriteria(Criteria)获得，目的提高性能，可以复用totalCount
	 * @param criteria
	 *            查询条件
	 * @param pageSize
	 *            每页记录数
	 * @param startIndex
	 *            每页记录的开始索引，可用Page类的getNextIndex获得
	 * @return Page对象
	 */
	public Page findPageByCriteria(final int totalCount,
			final Criteria criteria, final int pageSize, final int startIndex)
	{
		return findPageByCriteria(totalCount, criteria, pageSize, startIndex,
				null, false);
	}

	/**
	 * 查询符合Criteria的记录条数
	 * 
	 * @param criteria
	 * @return
	 */

	public int getCountByCriteria(final Criteria criteria)
	{
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {

					@Override
					public Object doInHibernate(Session arg0)
							throws HibernateException, SQLException
					{
						Integer count = (Integer) criteria.setProjection(
								Projections.rowCount()).uniqueResult();
						return count;
					}
				});
	}

	/**
	 * 创建DetachedCriteria
	 * 
	 * @param detachedCrieria
	 * @return 查询结果
	 */

	public List findByDetachedCriteria(final DetachedCriteria detachedCrieria)
	{
		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				Criteria criteria = detachedCrieria
						.getExecutableCriteria(session);
				return criteria.list();
			}

		});
	}

	/**
	 * 根据DetachedCriteria分页查询实体(排序)
	 * 
	 * @param totalCount
	 *            总记录数，可用getCountByDetachedCriteria(DetachedCriteria)获得，目的提高性能，可以复用totalCount
	 * @param detachedCriteria
	 *            查询条件
	 * @param pageSize
	 *            每页记录数
	 * @param startIndex
	 *            每页记录的开始索引，可用Page类的getNextIndex获得
	 * @param orderProperty
	 *            排序的属性
	 * @param isAsc
	 *            排序顺序
	 * @return Page对象
	 */

	public Page findPageByDetachedCriteria(final int totalCount,
			final DetachedCriteria detachedCriteria, final int pageSize,
			final int startIndex, final String orderProperty,
			final boolean isAsc)
	{
		return (Page) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				Criteria criteria = detachedCriteria
						.getExecutableCriteria(session);

				criteria.setProjection(null);
				if (!(orderProperty == null || orderProperty.equals("")))
				{
					if (isAsc)
					{
						criteria.addOrder(Order.asc(orderProperty));
					} else
					{
						criteria.addOrder(Order.desc(orderProperty));
					}
				}
				List items = criteria.setFirstResult(startIndex).setMaxResults(
						pageSize).list();
				Page page = new Page(items, totalCount, pageSize, startIndex);
				return page;
			}

		});
	}

	/**
	 * 查询符合DetachedCriteria的记录条数
	 * 
	 * @param DetachedCriteria
	 * @return
	 */

	public int getCountByDetachedCriteria(
			final DetachedCriteria detachedCriteria)
	{
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					@Override
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException
					{
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						return getCountByCriteria(criteria);
					}
				});
	}

	/**
	 * 根据DetachedCriteria分页查询实体
	 * 
	 * @param totalCount
	 * @param detachedCriteria
	 * @param pageSize
	 * @param startIndex
	 * @return
	 */
	public Page findPageByDetachedCriteria(final int totalCount,
			final DetachedCriteria detachedCriteria, final int pageSize,
			final int startIndex)
	{
		return findPageByDetachedCriteria(totalCount, detachedCriteria,
				pageSize, startIndex, null, false);
	}

	/**
	 * 创建HQL的Query对象
	 * 
	 * @param hql
	 * @param parameters
	 * @return
	 */
	private Query createQuery(Session session, final String hql,
			final Object... parameters)
	{
		Query query = session.createQuery(hql);
		if (parameters != null)
		{
			for (int i = 0; i < parameters.length; i++)
			{
				if (parameters[i] != null)
					query.setParameter(i, parameters[i]);
			}
		}
		return query;
	}

	/**
	 * 根据Query处理分页
	 * 
	 * @param totalCount
	 *            可以用getCountByQuery(hql, parameters)获得
	 * @param hql
	 * @param pageSize
	 * @param startIndex
	 * @param parameters
	 * @return
	 */

	public Page findPageByQuery(final Long totalCount, final String hql,
			final int pageSize, final int startIndex,
			final Object... parameters)
	{
		if (totalCount < 1)
		{
			return new Page(new ArrayList(0), 0);
		}
		return (Page) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				Query query = createQuery(session, hql, parameters);
				List items = query.setFirstResult(startIndex).setMaxResults(
						pageSize).list();
				Page ps = new Page(items, Integer.parseInt(totalCount
						.toString()), pageSize, startIndex);
				return ps;
			}
		});

	}

	public List findPageByQueryNoPage(final Long totalCount, final String hql,
			final int pageSize, final int startIndex,
			final Object... parameters)
	{
		if (totalCount < 1)
		{
			return null;
		}
		return (List) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				Query query = createQuery(session, hql, parameters);
				List items = query.setFirstResult(startIndex).setMaxResults(
						pageSize).list();
				return items;
			}
		});

	}

	public List findPageByQueryNoPage(final Long totalCount, final String hql,
			final int pageSize, final int startIndex)
	{
		if (totalCount < 1)
		{
			return null;
		}
		return (List) getHibernateTemplate().execute(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException
			{
				Query query = createQuery(session, hql);
				List items = query.setFirstResult(startIndex).setMaxResults(
						pageSize).list();
				return items;
			}
		});

	}

	// 处理HQL的工具方法，将HQL前面的select属性去掉，为了获得count
	private String removeSelect(String hql)
	{
		int beginPos = hql.toLowerCase().indexOf("from");
		return hql.substring(beginPos);
	}

	// 处理HQL的工具方法，将order去掉，为了获得count
	private static String removeOrders(String hql)
	{
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find())
		{
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 获得满足该HQL条件的记录count
	 * 
	 * @param hql
	 * @param values
	 * @return
	 */

	public Long getCountByQuery(final String hql, Object... values)
	{
		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, values);
		return (Long) countlist.get(0);
	}

	/**
	 * 获得满足该HQL条件的记录count
	 * 
	 * @param hql
	 * @return
	 */

	public Long getCountByQuery(final String hql)
	{
		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString);
		return (Long) countlist.get(0);
	}

	/**
	 * Find方式执行HQL
	 * 
	 * @param hql
	 * @return
	 */

	public List find(String hql)
	{
		return getHibernateTemplate().find(hql);
	}

	/**
	 * Find方式执行HQL
	 * 
	 * @param hql
	 * @param parameters
	 * @return
	 */

	public List find(String hql, Object... parameters)
	{
		return getHibernateTemplate().find(hql, parameters);

	}

	/**
	 * 适用Save Or Update不确定场景下的处理
	 * 
	 * @param entity
	 */
	public void saveOrUpdate(Object entity)
	{
		getHibernateTemplate().saveOrUpdate(entity);
	}

	/**
	 * 实体的模糊查询
	 * 
	 * @param entity
	 *            模糊查询条件组装的实体
	 * @return
	 */

	public List findByExample(Object entity)
	{
		return getHibernateTemplate().findByExample(entity);
	}

	/**
	 * 批量Insert,Update,Delete的处理
	 * 
	 * @param entityList
	 *            实体集合
	 * @param HDParam.OpType
	 *            操作类型枚举
	 * @return
	 */

	public int batchExcute(final List entityList, final BatchType batchType)
	{
		if (entityList == null || entityList.size() == 0)
		{
			return 0;
		}

		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					@Override
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException
					{
						Transaction tx = session.beginTransaction();
						int excuteCount = 0;
						switch (batchType)
						{
						case INSERT:
							excuteCount = batchSave(session, entityList);
							break;
						case UPDATE:
							excuteCount = batchUpdate(session, entityList);
							break;
						case DELETE:
							excuteCount = batchDelete(session, entityList);
							break;
						case SAVEORUPDATE:
							excuteCount = batchSaveOrUpdate(session, entityList);
							break;
						default:
							break;
						}
						tx.commit();
						return excuteCount;
					}
				});
	}

	private int batchSave(Session session, List entityList)
	{
		int i = 0;
		for (i = 0; i < entityList.size(); i++)
		{
			session.save(entityList.get(i));
			if (i % 50 == 0)
			{
				session.flush();
				session.clear();
			}
		}
		return i;
	}

	private int batchUpdate(Session session, List entityList)
	{
		int i = 0;
		for (i = 0; i < entityList.size(); i++)
		{
			session.update(entityList.get(i));
			if (i % 50 == 0)
			{
				session.flush();
				session.clear();
			}
		}
		return i;
	}

	private int batchDelete(Session session, List entityList)
	{
		int i = 0;
		for (i = 0; i < entityList.size(); i++)
		{
			session.delete(entityList.get(i));
			if (i % 50 == 0)
			{
				session.flush();
				session.clear();
			}
		}
		return i;
	}

	private int batchSaveOrUpdate(Session session, List entityList)
	{
		int i = 0;
		for (i = 0; i < entityList.size(); i++)
		{
			session.saveOrUpdate(entityList.get(i));
			if (i % 50 == 0)
			{
				session.flush();
				session.clear();
			}
		}
		return i;
	}

	/**
	 * 组装Criterion数组的工具方法
	 * 
	 * @param propertyNames
	 *            属性名称
	 * @param condition
	 *            比较条件
	 * @param propertyValue
	 *            属性值
	 * @return
	 */
	public Criterion[] createCriterion(String[] propertyNames,
			String[] condition, Object[] propertyValue)
	{
		if (propertyNames.length == condition.length
				&& propertyNames.length == condition.length)
		{

			Criterion[] criterion = new Criterion[propertyNames.length];

			for (int i = 0; i < propertyNames.length; i++)
			{
				if (condition[i] == "=")
					criterion[i] = Restrictions.eq(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == ">=")
					criterion[i] = Restrictions.ge(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == "<=")
					criterion[i] = Restrictions.le(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == ">")
					criterion[i] = Restrictions.gt(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == "<")
					criterion[i] = Restrictions.lt(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == "like")
					criterion[i] = Restrictions.like(propertyNames[i],
							propertyValue[i]);
				else if (condition[i] == "in")
					criterion[i] = Restrictions.in(propertyNames[i],
							(Object[]) propertyValue[i]);
			}
			return criterion;
		} else
		{
			return null;
		}
	}

	public String delete(String className, String constr)
	{
		if (constr != "")
			return "delete " + className + " where " + constr;
		else
			return "delete " + className;

	}

	/**
	 * HQL执行单条的Update或Delete
	 * 
	 * @param hql
	 * @param parameters
	 * @return
	 */

	public int updateByHql(final String hql, final Object[] parameters)
	{
		return getHibernateTemplate().execute(new HibernateCallback<Integer>() {

			@Override
			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException
			{

				Query query = createQuery(session, hql, parameters);
				return query.executeUpdate();
			}

		});
	}

	/**
	 * 原生sql查询(不推荐使用)
	 * 
	 * @param sql
	 * @param parameters
	 * @return
	 */
	public List<?> queryByNativeSql(final String sql,
			final Object... parameters)
	{
		return (List<?>) getHibernateTemplate().executeWithNativeSession(
				new HibernateCallback<Object>() {

					@Override
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException
					{
						SQLQuery query = session.createSQLQuery(sql);
						if (parameters != null)
						{
							for (int i = 0; i < parameters.length; i++)
							{
								query.setParameter(i, parameters[i]);
							}
						}
						return query.list();
					}
				});
	}

	public int udiByNativeSql(final String sql, final Object... parameters)
	{
		return (Integer) getHibernateTemplate().executeWithNativeSession(
				new HibernateCallback<Object>() {

					@Override
					public Integer doInHibernate(Session session)
							throws HibernateException, SQLException
					{
						SQLQuery query = session.createSQLQuery(sql);
						for (int i = 0; i < parameters.length; i++)
						{
							query.setParameter(i, parameters[i]);
						}
						return query.executeUpdate();
					}
				});
	}

	/**
	 * 通过HQL的方式更新数据
	 * 
	 * @param hql
	 * @param params
	 * @return
	 */
	public int updateOrDelHql(final String hql, final Object... params)
	{
		Integer result = (Integer) this.getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException
					{
						Query query = session.createQuery(hql);
						if (params != null)
						{
							int length = params.length;
							for (int i = 0; i < length; i++)
							{
								query.setParameter(i, params[i]);
							}
						}
						return query.executeUpdate();
					}

				});
		return result;
	}

	/**
	 * 批量保存
	 * 
	 * @param <T>
	 * @param list
	 * @return
	 */
	public <T extends Serializable> boolean saveOrUpdateAll(List<T> list)
	{
		this.getHibernateTemplate().saveOrUpdateAll(list);
		return true;
	}

	/**
	 * 通过HQL的方式查询数量
	 * 
	 * @param sql
	 * @param params
	 * @return
	 */
	public long hqlCount(String hql, Object... params)
	{
		return (Long) this.getHibernateTemplate().find(hql, params).get(0);
	}

	/**
	 * 通过HQL的方式进行数据查询，并进行分页
	 * 
	 * @param hql
	 *            HQL语句
	 * @param page
	 *            分布对象
	 * @param params
	 *            参数对象
	 * @return
	 */
	public <T> List<T> selectByPageHql(final String hql, final Page page,
			final Object... params)
	{
		return this.getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session arg0)
					throws HibernateException, SQLException
			{
				Query query = arg0.createQuery(hql);
				if (params != null)
				{
					int length = params.length;
					for (int i = 0; i < length; i++)
					{
						query.setParameter(i, params[i]);
					}
				}
				if (page != null)
				{
					query.setFirstResult(page.getStart());
					query.setMaxResults(page.getLimit());
				}
				List<T> list = query.list();
				return list;
			}
		});
	}

	/**
	 * 通过HQL的方式进行数据查询，并进行分页
	 * 
	 * @param hql
	 *            HQL语句
	 * @param clazz
	 *            查询结果对应的CLASS
	 * @param page
	 *            分布对象
	 * @param params
	 *            参数对象
	 * @return
	 */
	public <T> List<T> selectByPageSql(final String sql, final Page page,
			final Object... params)
	{
		return this.getHibernateTemplate().executeFind(new HibernateCallback() {

			public Object doInHibernate(Session arg0)
					throws HibernateException, SQLException
			{
				Query query = arg0.createSQLQuery(sql);
				if (params != null)
				{
					int length = params.length;
					for (int i = 0; i < length; i++)
					{
						query.setParameter(i, params[i]);
					}
				}
				if (page != null)
				{
					query.setFirstResult(page.getStart());
					query.setMaxResults(page.getLimit());
				}
				List<T> list = query.list();
				return list;
			}
		});
	}

	/**
	 * 通过SQL的方式进行数据查询
	 * 
	 * @param hql
	 *            HQL语句
	 * @param clazz
	 *            查询结果对应的CLASS
	 * @param params
	 *            参数对象
	 * @return
	 */
	public <T> List<T> findBySql(String sql, Object... params)
	{
		return selectByPageSql(sql, null, params);
	}

}
